/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef TODO_COMPILER_H
#define TODO_COMPILER_H
#include <stack>
#include "types.h"
#include "location.hh"
#include "opcodes.h"
#include "modules/module.h"

class todo_driver;

#define NEW_RESULT_IF_NOT_EXISTS(x)              \
	if ((x).data_type != todo::TODO_RET_VALUE) { \
		(x).result = new ValueResult;            \
	}
	
#define SET_RESULT(x, y)            \
	(x).data_type = TODO_RET_VALUE; \
	(x).result = y;                 \
	(x).result_flag = REFERENCED;

namespace todo { 

class Compiler {
	// Opcode list
	static std::vector<todo::opcode::Opcode*> opcode_list;
	// Arguments call stack
	std::stack<ArgList*> call_args;
	// Arguments declaration stack
	std::stack<FunctionArgList*> decl_args;
	// Modules
	TodoModules modules;
public:
	// Compiled files counter
	static int count;
	// Scope handler
	Scopes scopes;
	// Jump stacks
	std::stack<std::stack<opcode::Opcode*> > jmps;
	// Break stacks
	std::stack<std::stack<opcode::Opcode*> > brk_jmps;
	const yy::location* location;
	bool is_file;

	Compiler();
	~Compiler() { }

	void addOpcode(todo::opcode::Opcode*);
	unsigned int get_op_num(void);
	opcode::Opcode* get_opcode(unsigned int);
	void error(const char*) const;
	void error(std::string) const;
	void finalize(void);
	void begin_fcall(void);
	void begin_fdecl(void);
	void add_arg(todo::Data&);
	void add_decl_arg(todo::Data&, todo::Data&);
	void import(Data&);
	
	void check_exists_var(Data*, Data&, Data&);
	void register_var(Var*);
	void register_func(Function*);
	bool check_type_var(const std::string*, const todo::Data&);
	bool check_type_value(const Type*, const todo::Data&);
	bool check_func_exists(const todo::Data&);
	Var* get_var(const std::string*);	
	
	void add_module(Data&, Data&);
	void end_module(Data&);
	void assign(todo::Data&, todo::Data&);
	void minus(Data&, Data&, Data*, bool);
	void plus(Data&, Data&, Data*, bool);
	void div(Data&, Data&, Data*, bool);
	void mult(Data&, Data&, Data*, bool);
	void mod(Data&, Data&, Data*, bool);
	void declare_var(Data&, Data&, Data*);
	void println(todo::Data&, bool);
	void method_call(todo::Data&, todo::Data&, todo::Data&);
	void static_method_call(todo::Data&, todo::Data&, todo::Data&);
	void slice(todo::Data&, todo::Data*, todo::Data*);
	void less(todo::Data&, todo::Data&);
	void greater(todo::Data&, todo::Data&);
	void less_equal(todo::Data&, todo::Data&);
	void greater_equal(todo::Data&, todo::Data&);
	void subscript(todo::Data&, todo::Data&);
	void pre_increment(todo::Data&);
	void pre_decrement(todo::Data&);
	void pos_increment(todo::Data&);
	void pos_decrement(todo::Data&);
	void if_cond(todo::Data&, todo::Data&);
	void if_elsif(todo::Data&, todo::Data&);
	void if_end_block(todo::Data&);
	void end_if(void);
	void equal(todo::Data&, todo::Data&);
	void is_not_equal(todo::Data&, todo::Data&);
	void new_vector(todo::Data&);
	void vector_add_item(todo::Data&, todo::Data&);
	void new_map(todo::Data&);
	void map_add_item(todo::Data&, todo::Data&, todo::Data&);
	void begin_loop(todo::Data&);
	void start_loop(todo::Data&);
	void end_loop(todo::Data&, todo::Data&);
	void add_func(todo::Data*, todo::Data&);
	void end_func(todo::Data&);
	void func_call(Data&, Data& result);
	void negative(todo::Data&);
	void return_stmt(todo::Data*);
	void exit_stmt(Data*);
	void break_stmt(Data*);
	void xor_expr(Data&, Data&, Data*, bool);
	void or_expr(Data&, Data&, Data*, bool);
	void and_expr(Data&, Data&, Data*, bool);
	void shift_left(Data&, Data&, Data*, bool);
	void shift_right(Data&, Data&, Data*, bool);
};

} // todo

#endif // TODO_COMPILER_H
